package io.mft.as.ar.gateway.client.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import com.thinkive.tbascli.TBASClient;

import cn.hutool.core.util.ObjectUtil;
import io.mft.as.ar.gateway.client.BusClient;
import io.mft.as.ar.gateway.client.GateWayManager;
import io.mft.as.ar.gateway.client.NetAddress;
import io.mft.as.ar.gateway.client.Server;
import io.mft.as.ar.gateway.config.BusIdConfig;
import io.mft.as.ar.gateway.constants.GatewayConstants;
import io.mft.as.ar.gateway.pool.BusPool;
import io.mft.as.ar.gateway.pool.client.PoolAsClientV2;
import io.mft.as.ar.gateway.result.Result;
import io.mft.commons.config.ConfigProvider;
import io.mft.commons.function.response.kv.KVMap;
import io.mft.commons.function.response.kv.PageObject;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class BusClientV2 implements BusClient
{
    
    
    private int           branch     = 0;
    
    private String        serverName = "";                                 //系统编码
    private final static int RANDOM_BASE = 10;                                                                 
    public BusClientV2()
    {
        
    }
    
    public void setServerName(String serverName)
    {
        this.serverName = serverName;
    }
    
    public void setBranch(int branch)
    {
        this.branch = branch;
    }
    
    /**
     *
     * 描述：
     * 作者：李炜
     * 时间：Dec 15, 2013 5:02:13 AM
     * @return
     * @throws Exception
     */
    private TBASClient getClient() throws Exception
    {
        Server server = GateWayManager.getServer(serverName);
        if ( server == null )
        {
            throw new Exception("获取服务器失败，失败原因：未获取到可用的连接");
        }
        
        if ( 1 == server.getMode() )
        {
            //长连接
            return getPoolClient(server);
        }
        else
        {
            //短连接
            return getAsClient(server);
        }
    }
    
    /**
     *
     * 描述：
     * 作者：李炜
     * 时间：Dec 15, 2013 5:47:35 AM
     * @param server
     * @return
     * @throws Exception
     */
    private TBASClient getPoolClient(Server server) throws Exception
    {
        BusPool busPool = server.getRmdASPool();
        return (TBASClient) busPool.getClient();
    }
    
    /**
     *
     * 描述：
     * 作者：李炜
     * 时间：Dec 15, 2013 5:47:48 AM
     * @param server
     * @return
     * @throws Exception
     */
    private TBASClient getAsClient(Server server) throws Exception
    {
        TBASClient client;
        NetAddress netAddress = server.getRmdServer();
        if ( netAddress == null )
        {
            throw new Exception("获取服务器失败，失败原因：未获取到可用的连接");
        }
        
        try
        {
            client = new TBASClient();
        }
        catch (Throwable t)
        {
            log.error("创建TBAS连接失败，失败原因：" + t.getMessage());
            throw new Exception(t);
        }
        client.setConnect(netAddress.getIP(), netAddress.getPort());
        boolean isConnect = client.connect();
        if ( !isConnect )
        {
            throw new Exception("获取服务器失败，失败原因：服务器【" + server.getGateWayName() + "】创建连接【" + netAddress.getIP() + ":"
                    + netAddress.getPort() + "】失败");
        }
        //设置超时响应时间
        int recvTimeout = server.getRecvTimeout();
        if ( recvTimeout > 0 )
        {
            client.setRecvTimeout(recvTimeout);
        }
        
        String key = server.getKey();
        if ( ObjectUtil.isNotEmpty(key) )
        {
            client.setKey(key);
        }
        
        return client;
    }
	public static String getRandomStr(int length) {
		Random random = new Random();
		StringBuilder randStr = new StringBuilder();
		for (int i = 0; i < length; i++) {
			String randItem = String.valueOf(random.nextInt(RANDOM_BASE));
			randStr.append(randItem);
		}
		return randStr.toString();
	}
    public Result invoke(int funcNo, Map paraMap) throws Exception
    {
        Result result = new Result();
       
        int senderId = Integer.valueOf(getRandomStr(4));
        
        TBASClient client = null;
        Map<String, List<KVMap>> results = null;
        ArrayList<String> pageDsNameArr = null;
        int errNo = -1;
        try
        {
            client = getClient();
            if ( client == null )
            {
                throw new Exception("未获取到可用的连接，请检查后端服务是否可用");
            }
            client.freePack();
            
            //设置分包头(支号和功能号)
            client.setHead(getBranch(funcNo), funcNo);
            //设置请求参数个数
            client.setRange(paraMap.size(), 1);
            //设置发送ID(流水号)
            client.setSenderId(senderId);
            //设置子系统编码
            int systemNo = BusIdConfig.getSystemNo(serverName);
            client.setSystemNo(systemNo);
            
            //设置通讯密钥
            //client.setKey(key);
            
            Object[] itemNameArray = paraMap.keySet().toArray();
            //按顺序设置请求参数
            for (int i = 0; i < itemNameArray.length; i++)
            {
                String name = (String) itemNameArray[i];
                client.addField(name);
            }
            //按顺序设置参数值，顺序必须和名称对应
            for (int i = 0; i < itemNameArray.length; i++)
            {
                String value = String.valueOf(paraMap.get(itemNameArray[i]));
                client.addValue(value);
            }
            
            long t1 = System.currentTimeMillis();
            //调用业务
            client.callServer();
            
            errNo = client.getErrorNo();
            String errInfo = client.getErrorMsg();
            result.setErr_no(errNo);
            result.setErr_info(errInfo);
            
            if ( errNo >= 0 )//成功，组装数据
            {
                results = new HashMap<String, List<KVMap>>();
                
                int dsCount = client.getDataSetCount();
                //result.setDataSetNum(dsCount);
                
                //result.setFirstDataSetName(client.getDataSetName());//保存第一个结果集名称
                
                String[] dsNameArr = new String[dsCount];//保存结果集名称
                
                pageDsNameArr = new ArrayList<String>();//保存分页结果集名称
                
                List<KVMap> dataList = null;
                for (int m = 0; m < dsCount; m++)
                {
                    dataList = new ArrayList<>();
                    
                    client.setCurDataSet(m);
                    //当前结果集字段数量
                    int fieldCount = client.getFieldCount();
                    //当前结果集总行数
                    int recordCount = client.getRecordCount();
                    //结果集名称
                    String name = client.getDataSetName();
                    dsNameArr[m] = name;
                    
                    //将字段放到数组
                    String[] fieldNameArray = new String[fieldCount];
                    for (int i = 0; i < fieldCount; i++)
                    {
                        fieldNameArray[i] = client.getFieldName((short) i);
                    }
                    
                    //循环结果集，将结果集的值放到list
                    while (client.next())
                    {
                        KVMap rowMap = new KVMap();
                        for (int i = 0; i < fieldNameArray.length; i++)
                        {
                            String field = fieldNameArray[i];
                            String value = client.fieldByName(field);
                            rowMap.set(field.trim(), value);
                        }
                        dataList.add(rowMap);
                    }
                    //result.setResult(name, dataList);
                    results.put(name, dataList);
                    
                    /**
                     * 判断当前结果集是否属于分页信息，分页信息包含当前页、每页显示的记录数、以及总记录页几个字段
                     */
                    List tempFieldNameArray = Arrays.asList(fieldNameArray);
                    if ( tempFieldNameArray.contains(GatewayConstants.FIELD_CURR_PAGE)
                            && tempFieldNameArray.contains(GatewayConstants.FIELD_PAGE_COUNT)
                            && tempFieldNameArray.contains(GatewayConstants.FIELD_TOTAL_ROWS) )
                    {
                        pageDsNameArr.add(name.substring(0, name.length() - 1));
                    }
                    tempFieldNameArray = null;
                }
                
                /**
                 * 遍历结果集，判断结果集中是否有分页数据
                 * 分页数据是作为两个结果集返回的，结果集名称0保存的是查询数据，结果集名称1保存的是分页信息
                 * 通过这个规则可以根据结果集名称判断当前结果集名称是否分页
                 */
                for (int i = 0; i < dsNameArr.length; i++)
                {
                    String pageDsName = dsNameArr[i].substring(0, dsNameArr[i].length() - 1);
                    if ( pageDsNameArr.contains(pageDsName) )
                    {
                        if ( result.getDsNameArr().contains(pageDsName) )
                        {
                            continue;
                        }
                        List<KVMap> resultData = results.remove(pageDsName + "0");
                        List<KVMap> pageList = results.remove(pageDsName + "1");
                        if ( resultData == null || pageList == null || pageList.isEmpty() )
                        {
                            continue;
                        }
                        
                        KVMap pageData = (KVMap) pageList.get(0);
                        int curr_page = pageData.getInt(GatewayConstants.FIELD_CURR_PAGE);//当前页
                        int page_count = pageData.getInt(GatewayConstants.FIELD_PAGE_COUNT);//一页显示记录数
                        int total_rows = pageData.getInt(GatewayConstants.FIELD_TOTAL_ROWS);//总记录数
                        PageObject<KVMap> pageObject=new PageObject<>(curr_page, page_count, total_rows);
                        pageObject.getList().addAll(resultData);
                        result.setResult(pageDsName, pageObject);
                    }
                    else
                    {
                        List<KVMap> resultData = results.remove(dsNameArr[i]);
                        result.setResult(dsNameArr[i], resultData);
                    }
                }
                
            }
            else
            {
                log.warn("功能号【" + funcNo + "】接口调用出错，错误代码" + errNo + "，错误消息：" + errInfo);
            }
            
            long t2 = System.currentTimeMillis();
            int invokeTime = Integer.valueOf(ConfigProvider.get("system.invokeMaxTime", "60"));//大于指定的时间用
            if ( (t2 - t1) / 1000 > invokeTime )
            {
                log.info("功能号【" + funcNo + "】接口调用时间：" + (t2 - t1) + "毫秒");
            }
            else
            {
                log.debug("功能号【" + funcNo + "】接口调用时间：" + (t2 - t1) + "毫秒");
            }
        }
        catch (Exception ex)
        {
            log.error("功能号【" + funcNo + "】接口调用出错，错误消息：", ex);
            throw new Exception(ex);
        }
        finally
        {
            if ( pageDsNameArr != null )
            {
                pageDsNameArr.clear();
            }
            if ( results != null )
            {
                results.clear();
            }
            /**
             * liwei modify by 20170908
             * 处理大于-4000小于-3000的错误消息，这些错误在长连接模式下，连接必须释放，不允许再还回池子
             * 错误消息有
             -3001     创建连接对象失败
             -3002     连接服务器失败
             -3003     接收会话信息失败
             -3004     接收登陆响应失败
             -3005     服务器登陆验证失败
             -3006     未连接服务器

             -3010     请求行数量不正确
             -3011     请求字段数量不正确
             -3012     请求值数量不正确
             -3013     接收响应包出错或超时
             -3014     响应数据流水号不正确
             -3015     响应包数据格式不正确
             */
            boolean isUsable = true;//socket是否可用　false:不可用
            if ( errNo > -4000 && errNo < -3000 )
            {
                isUsable = false;
            }
            close(client, isUsable);
        }
        return result;
    }
    private int getBranch(int funcNo)
    {
        int branch = this.branch;
        if ( branch <= 0 )
        {
            String temp = String.valueOf(funcNo);
            if ( temp.length() >= 4 )
            {
                branch = new Integer(temp.substring(0, 4));
            }
        }
        return branch;
    }
    
    /**
     *
     * @描述:
     * @作者: liwei
     * @创建日期: 2017年9月8日 下午8:20:59
     * @param client
     * @param isUsable socket是否可用 false:不可用
     */
    private void close(TBASClient client, boolean isUsable)
    {
        if ( client != null )
        {
            //长连接模式下，接口调用成功需将连接还回池中，否则直接释放　
            if ( 1 == GateWayManager.getMode(serverName) )
            {
                if ( isUsable )
                {
                    client.close();
                }
                else
                {
                    if ( client instanceof PoolAsClientV2 )
                    {
                        ((PoolAsClientV2) client).setIsUsable(isUsable);
                        client.close();
                    }
                }
            }
            else
            {
                client.freePack();
                client.disConnect();
                client.close();
            }
        }
    }
}
